package edu.polsl.bio.str.node.logic;

import edu.polsl.bio.str.common.dto.StrMatch;
import edu.polsl.bio.str.common.exception.ManagerException;
import edu.polsl.bio.str.common.messaging.StrDotsPayload;
import edu.polsl.bio.str.common.messaging.StrJobPayload;
import edu.polsl.bio.str.common.util.ProgressHelper;
import edu.polsl.bio.str.common.util.SerializationUtils;
import edu.polsl.bio.str.common.vo.JobData;
import edu.polsl.bio.str.node.delegate.DotPlotServiceDelegate;
import edu.polsl.bio.tools.str.StrMatcher;
import edu.polsl.bio.tools.str.StrMatcherConfig;
import edu.polsl.bio.tools.str.StrRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.LinkedList;
import java.util.List;

/**
 * //todo class description
 * <p/>
 * Created on: Jun 12, 2011 10:55:51 AM <br/>
 * <a href="http://www.polsl.pl">www.polsl.pl</a>
 *
 * @author lukasz.olczak@polsl.pl
 */
public class DotPlotWorker implements Runnable {
  /**
   * slf4j Logger.
   */
  private static final Logger log = LoggerFactory.getLogger(DotPlotWorker.class.getName());

  private StrJobPayload job;

  private StrMatcher strMatcher;

  private DotPlotServiceDelegate dotPlotServiceDelegate;

  private NodeContext nodeContext;

  private ConnectionLossHandler connectionLossHandler;

  private JobData jobData;

  public DotPlotWorker(StrJobPayload job) {
    this.job = job;
  }

  @Override
  public void run() {
    nodeContext.incNoJobs();
    try {
      jobData = (JobData) SerializationUtils.decompressObject(job.getJobData());
      log.info("Starting dot plot computing (jobId={}),  ref=<{},{}>, query=<{},{}>",
               new Object[]{job.getJobId(), jobData.getRefStrs().get(0).getStartIndex(),
                            jobData.getRefStrs().get(jobData.getRefStrs().size() - 1).getStartIndex(),
                            jobData.getQueryStrs().get(0).getStartIndex(),
                            jobData.getQueryStrs().get(jobData.getQueryStrs().size() - 1).getStartIndex()});

      initMatcher();
      List<StrMatch> dots = new LinkedList<StrMatch>();
      List<StrMatch> reverseDots = new LinkedList<StrMatch>();
      ProgressHelper progress = new ProgressHelper(jobData.getRefStrs().size(), 100);
      int i = 0;
      for (StrRecord ref : jobData.getRefStrs()) {
        if (progress.collect()) {
          log.debug("Progress of job {} is {}", job.getJobId(), progress.getProgressPercent());
        }
        for (StrRecord query : jobData.getQueryStrs()) {
          if (strMatcher.match(ref, query)) {
            dots.add(new StrMatch(ref, query));
          }
          StrRecord qReverse = query.getReverseStr();
          if (strMatcher.match(ref, qReverse)) {
            reverseDots.add(new StrMatch(ref, qReverse));
          }
        }
        i++;
      }
      sendResults(dots, reverseDots);
    } catch (Exception e) {
      log.error("Exception occurred during dot plot computing", e);
    } finally {
      nodeContext.decNoJobs();
    }
  }

  private void sendResults(List<StrMatch> dots, List<StrMatch> reverseDots) {
    StrDotsPayload payload = new StrDotsPayload();
    payload.setConfig(jobData.getConfig());
    payload.setDots(dots);
    payload.setReverseDots(reverseDots);
    payload.setJobId(job.getJobId());
    payload.setNodeId(nodeContext.getNodeId());
    try {
      dotPlotServiceDelegate.sendResults(payload);
      log.debug("Results sent");
    } catch (ManagerException e) {
      log.error("Can't send results " + payload + ", exception occurred", e);
      connectionLossHandler.handleConnectionLoss();
    }
  }

  private void initMatcher() {

    StrMatcherConfig config = jobData.getConfig();
    StrRecord example = jobData.getRefStrs().get(0);
    int lSize = config.isUseLeftFlank() ? example.getLeftFlank().length() : -1;
    int rSize = config.isUseRightFlank() ? example.getRightFlank().length() : -1;
    log.debug("Creating strMatcher with parameters: lSize={}, rSize={}, useLeftFlank={}, useRightFlank={}, match={}, " +
              "mismatch={}, indel={}, cutOff={}", new Object[]{lSize, rSize, config.isUseLeftFlank(),
                                                               config.isUseRightFlank(), config.getMatch(),
                                                               config.getMismatch(), config.getIndel(),
                                                               config.getCutoff()});
    strMatcher = new StrMatcher(lSize, rSize, config.isUseLeftFlank(), config.isUseRightFlank(), config.getMatch(),
                                config.getMismatch(), config.getIndel(), config.getCutoff());
  }

  public void setDotPlotServiceDelegate(DotPlotServiceDelegate dotPlotServiceDelegate) {
    this.dotPlotServiceDelegate = dotPlotServiceDelegate;
  }

  public void setNodeContext(NodeContext nodeContext) {
    this.nodeContext = nodeContext;
  }

  public void setConnectionLossHandler(ConnectionLossHandler connectionLossHandler) {
    this.connectionLossHandler = connectionLossHandler;
  }
}
